use napi_derive_ohos::napi;
use napi_ohos::Either;
use sha2::{
    Digest,
    digest::{consts::U32, generic_array::GenericArray},
};

#[napi]
#[derive(Default)]
pub struct Sha256 {
    inner: sha2::Sha256,
    digest: Option<GenericArray<u8, U32>>,
}

#[napi]
impl Sha256 {
    /**
     * @param {string | Uint8Array} buf Input to calculate the hash.
     * @return {string} Hexadecimal string of the hash.
     */
    #[napi]
    pub fn hash(buf: Either<String, &[u8]>) -> String {
        let mut hash = sha2::Sha256::default();
        hash.update(buf);
        let result = hash.finalize();
        format!("{result:x}")
    }

    /**
     * @return {Sha256} Create new hasher instance.
     */
    #[napi]
    pub fn create() -> Self {
        Self::default()
    }

    /**
     * @param {string | Uint8Array} buf New input to process.
     */
    #[napi]
    pub fn update(&mut self, buf: Either<String, &[u8]>) {
        if self.digest.is_some() {
            self.digest = None;
        }
        self.inner.update(buf);
    }

    /**
     * This function also reset the internal state.
     * @return {string} Hexadecimal string of the hash.
     */
    #[napi]
    pub fn hex(&mut self) -> String {
        let result = self.digest.get_or_insert(self.inner.finalize_reset());
        format!("{result:x}")
    }

    /**
     * This function also reset the internal state.
     * @return {Array<number>} Array of number of the hash.
     */
    #[inline]
    fn buffer(&mut self) -> Vec<u32> {
        let result = self.digest.get_or_insert(self.inner.finalize_reset());
        result.into_iter().map(|e| *e as u32).collect()
    }

    /**
     * This function also reset the internal state.
     * @return {Array<number>} Array of number of the hash.
     */
    #[napi]
    pub fn digest(&mut self) -> Vec<u32> {
        self.buffer()
    }

    /**
     * This function also reset the internal state.
     * @return {Array<number>} Array of number of the hash.
     */
    #[napi]
    pub fn array(&mut self) -> Vec<u32> {
        self.buffer()
    }

    /**
     * This function also reset the internal state.
     * @return {Array<number>} Array of number of the hash.
     */
    #[napi]
    pub fn array_buffer(&mut self) -> Vec<u32> {
        self.buffer()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn case1() {
        let mut hasher = Sha256::create();
        hasher.update(Either::B(b"hello "));
        hasher.update(Either::B(b"world"));
        let hash = hasher.hex();
        assert_eq!(
            "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
            hash
        );
    }
}
